home *** CD-ROM | disk | FTP | other *** search
- /*
- * This file is part of ixemul.library for the Amiga.
- * Copyright (C) 1991, 1992 Markus M. Wild
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * select.c,v 1.1.1.1 1994/04/04 04:30:33 amiga Exp
- *
- * select.c,v
- * Revision 1.1.1.1 1994/04/04 04:30:33 amiga
- * Initial CVS check in.
- *
- * Revision 1.2 1993/11/05 22:00:59 mwild
- * extensively rewritten to work better along with inet.library
- *
- * Revision 1.1 1992/05/14 19:55:40 mwild
- * Initial revision
- *
- */
-
- #define _KERNEL
- #include "ixemul.h"
- #include "kprintf.h"
-
- #include <sys/time.h>
- #include <unistd.h>
- #include <string.h>
-
- #include "select.h"
-
- #define __time_req (u.u_time_req)
- #define __tport (u.u_sync_mp)
-
- static void handle_select_port(void)
- {
- struct StandardPacket *prw;
-
- while ((prw = GetPacket(u.u_select_mp)))
- prw->sp_Pkt.dp_Port = 0;
- }
-
- static void inline setcopy(int nfd, u_int *ifd, u_int *ofd)
- {
- /* this procedure is here, because it's "normal" that if you only
- * want to select on fd 0,1,2 eg. you only pass a long to select,
- * not a whole fd_set, so we can't simply copy over results in the
- * full size of an fd_set.. */
-
- /* we have to copy that many longs... */
- nfd = (nfd+31) >> 5;
- while (nfd--) *ofd++ = *ifd++;
- }
-
- int
- aselect(int nfd, fd_set *ifd, fd_set *ofd, fd_set *efd, struct timeval *timeout, long mask)
- {
- struct file *f;
- int i, waitin, waitout, waitexc, dotout;
- int result, ostat, sigio;
- u_int wait_sigs;
- struct timeval end_time;
- int skipped_wait;
- struct user *p = &u;
- u_long recv_wait_sigs = 0;
- u_long net_nfds;
-
- if (CURSIG (p))
- {
- *(p->u_errno) = EINTR;
- return -1;
- }
-
- /* as long as I don't support anything similar to a network, I surely
- * won't get any exceptional conditions, so *efd is mapped into
- * *ifd, if it's set. */
-
- /* first check, that all included descriptors are valid and support
- * the requested operation. If the user included a request to wait
- * for a descriptor to be ready to read, while the descriptor was only
- * opened for writing, the requested bit is immediately cleared
- */
- waitin = waitout = waitexc = 0;
- if (nfd > NOFILE) nfd = NOFILE;
-
- for (i = 0; i < nfd; i++)
- {
- if (ifd && FD_ISSET(i, ifd) && (f = p->u_ofile[i]))
- {
- if (!f->f_read || !f->f_select)
- FD_CLR(i, ifd);
- else
- ++waitin;
- }
- if (ofd && FD_ISSET(i, ofd) && (f = p->u_ofile[i]))
- {
- if (!f->f_write || !f->f_select)
- FD_CLR(i, ofd);
- else
- ++waitout;
- }
- if (efd && FD_ISSET(i, efd) && (f = p->u_ofile[i]))
- {
- /* question: can an exceptional condition also occur on a
- * write-only fd?? */
- if (!f->f_read || !f->f_select)
- FD_CLR(i, efd);
- else
- ++waitexc;
- }
- }
-
- if ((dotout = (timeout && timerisset(timeout))))
- {
- /* remember the time we have to leave (timeout) */
- syscall(SYS_gettimeofday, &end_time, 0);
- end_time.tv_usec += timeout->tv_usec;
- /* this conversion should be cheaper than a division and a modulo.. */
- while (end_time.tv_usec >= 1000000)
- {
- end_time.tv_usec -= 1000000;
- end_time.tv_sec++;
- }
- end_time.tv_sec += timeout->tv_sec;
- }
-
- /* have to make sure we can clean up the timer-request ! */
- ostat = p->p_stat;
- p->p_stat = SSLEEP;
- p->p_wchan = (caddr_t) select; /* will once be an own variable */
- p->p_wmesg = "select";
-
- for (skipped_wait = 0; ; skipped_wait=1)
- {
- fd_set readyin, readyout, readyexc;
- fd_set netin, netout, netexc; /* used by ixnet.library */
- int tout, readydesc, cmd;
-
- FD_ZERO(&readyin);
- FD_ZERO(&readyout);
- FD_ZERO(&readyexc);
-
- if (u.u_ixnetbase)
- {
- FD_ZERO(&netin);
- FD_ZERO(&netout);
- FD_ZERO(&netexc);
- net_nfds = 0;
- }
-
- tout = readydesc = 0;
-
- /* have to always wait for the `traditional' ^C and the library internal
- * sleep_sig as well */
- wait_sigs = SIGBREAKF_CTRL_C | (1 << p->u_sleep_sig) | mask;
-
- handle_select_port();
-
- if (skipped_wait)
- {
- cmd = SELCMD_CHECK;
-
- /* have all watched files get prepared for selecting */
- for (i = 0; i < nfd; i++)
- {
- if (ifd && FD_ISSET (i, ifd) && (f = p->u_ofile[i]))
- wait_sigs |= f->f_select (f, SELCMD_PREPARE, SELMODE_IN, &netin, &net_nfds);
- if (ofd && FD_ISSET (i, ofd) && (f = p->u_ofile[i]))
- wait_sigs |= f->f_select (f, SELCMD_PREPARE, SELMODE_OUT, &netout, &net_nfds);
- if (efd && FD_ISSET (i, efd) && (f = p->u_ofile[i]))
- wait_sigs |= f->f_select (f, SELCMD_PREPARE, SELMODE_EXC, &netexc, &net_nfds);
- }
- if (!(u.p_sigignore & sigmask (SIGIO)))
- {
- struct file **f = u.u_ofile;
-
- for (i = 0; i < u.u_lastfile; i++)
- if (f[i] && (f[i]->f_flags & FASYNC) && !(ifd && FD_ISSET(i, ifd)))
- wait_sigs |= f[i]->f_select (f[i], SELCMD_PREPARE, SELMODE_IN, &netin, &net_nfds);
- }
-
- /* note: we never post a timeout request of less than SELTIMEOUT, so
- * select() precision depends on SELTIMEOUT. However, considering that
- * this call emulates a Unix syscall, this comes quite near to Unix
- * precision, if not better ;-)) */
- __time_req->tr_time.tv_sec = 0;
- __time_req->tr_time.tv_usec = SELTIMEOUT;
- __time_req->tr_node.io_Command = TR_ADDREQUEST;
- SendIO((struct IORequest *)__time_req);
- /* clear the bit, it's used for sync packets too, and might be set */
- SetSignal (0, 1 << __tport->mp_SigBit);
- wait_sigs |= 1 << __tport->mp_SigBit;
-
- /* now wait for all legally possible signals, this includes BSD
- * signals (but want at least one signal set!) */
- if (u.u_ixnetbase)
- recv_wait_sigs = netcall(NET_waitselect, wait_sigs,
- &netin, &netout, &netexc, net_nfds);
- else
- while (!(recv_wait_sigs = Wait (wait_sigs))) ;
-
- /* IMPORTANT: unqueue the timer request BEFORE polling the fd's,
- * or __wait_packet() will treat the timer request
- * as a packet... */
-
- if (! CheckIO ((struct IORequest *)__time_req))
- AbortIO ((struct IORequest *)__time_req);
- WaitIO ((struct IORequest *)__time_req);
-
- handle_select_port();
- }
- else
- cmd = SELCMD_POLL;
-
- /* no matter what caused Wait() to return, wait for all requests to
- * complete (we CAN'T abort a DOS packet, sigh..) */
-
- /* collect information from the file descriptors */
- for (i = 0; i < nfd; i++)
- {
- if (ifd && FD_ISSET (i, ifd) && (f = p->u_ofile[i])
- && f->f_select (f, cmd, SELMODE_IN, &netin, NULL))
- {
- FD_SET (i, &readyin);
- ++ readydesc;
- }
- if (ofd && FD_ISSET (i, ofd) && (f = p->u_ofile[i])
- && f->f_select (f, cmd, SELMODE_OUT, &netout, NULL))
- {
- FD_SET (i, &readyout);
- ++ readydesc;
- }
- if (efd && FD_ISSET (i, efd) && (f = p->u_ofile[i])
- && f->f_select (f, cmd, SELMODE_EXC, &netexc, NULL))
- {
- FD_SET (i, &readyexc);
- ++ readydesc;
- }
- }
-
- /* we have a timeout condition, if readydesc == 0, dotout == 1 and
- * end_time < current time */
- if (!readydesc && dotout)
- {
- struct timeval current_time;
-
- syscall(SYS_gettimeofday, ¤t_time, 0);
- tout = timercmp (&end_time, ¤t_time, <);
- }
-
- sigio = 0;
- if (!(u.p_sigignore & sigmask (SIGIO)))
- {
- struct file **f = u.u_ofile;
-
- for (i = 0; i < u.u_lastfile; i++)
- if (f[i] && (f[i]->f_flags & FASYNC) && !(ifd && FD_ISSET(i, ifd)))
- if (f[i]->f_select (f[i], cmd, SELMODE_IN, &netin, NULL))
- sigio = 1;
- }
-
- if (readydesc || tout || (timeout && !timerisset(timeout)))
- {
- if (ifd) setcopy(nfd, (u_int *)&readyin, (u_int *)ifd);
- if (ofd) setcopy(nfd, (u_int *)&readyout, (u_int *)ofd);
- if (efd) setcopy(nfd, (u_int *)&readyexc, (u_int *)efd);
- result = readydesc; /* ok for tout, since then readydesc is already 0 */
- break;
- }
-
- if (sigio || (recv_wait_sigs & (SIGBREAKF_CTRL_C | (1 << p->u_sleep_sig) | mask)))
- {
- result = -1;
- break;
- }
- }
-
- p->p_wchan = 0;
- p->p_wmesg = 0;
- p->p_stat = ostat;
- if (recv_wait_sigs == (u_long)-1)
- return -1;
- /* need special processing for ^C here, as that is completely disabled
- when we're SSLEEPing */
- if (recv_wait_sigs & SIGBREAKF_CTRL_C)
- _psignal(FindTask(0), SIGINT);
- if (sigio)
- _psignal(FindTask(0), SIGIO);
- setrun(FindTask(0));
-
- if (result == -1)
- /* have to set this here, since errno can be changed in signal handlers */
- *(p->u_errno) = EINTR;
-
- return result;
- }
-
- int
- select(int nfd, fd_set *ifd, fd_set *ofd, fd_set *efd, struct timeval *timeout)
- {
- return aselect(nfd, ifd, ofd, efd, timeout, 0);
- }
-